﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Services;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Data.SqlClient;
using System.Data;
using System.Web.Caching;
using System.Configuration;

namespace Note
{
    public partial class Note : System.Web.UI.Page
    {
        public const string COOKIE_NAME = "NoteSimple";
        public string connString = ConfigurationManager.ConnectionStrings["SQL"].ConnectionString;

        /// <summary>
        /// This is to help us identify the entries we added into Cache
        /// </summary>
        public const string CACHE_PREFIX = "note_";

        [WebMethod]
        public static void AutoSaveContent(string data, string userHash)
        {
            Save(data, userHash);            
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            string key = string.Empty;

            //if (Request.Url.Segments.Length < 3 || string.IsNullOrWhiteSpace(Request.Url.Segments[2]) || Request.QueryString["New"]=="1")
            if (String.IsNullOrWhiteSpace(Request.QueryString["uKey"]) || Request.QueryString["New"] == "1")            
            {
                //Check cookie
                if (Request.Cookies.Count > 0 && Request.Cookies[COOKIE_NAME] != null && Request.QueryString["New"] != "1")
                {
                    HttpCookie cookie = Request.Cookies[COOKIE_NAME];
                    if (!string.IsNullOrWhiteSpace(cookie.Value))
                    {
                        key = cookie.Value;
                    }
                }

                //Create new key
                if (key == string.Empty)
                {
                    key = GenerateKey();
                }

                //redirect to the new url
                Response.Redirect(string.Format("/Note/Note.aspx?uKey={0}",key));
            }
            else
            {
                //Retrive data
                string NoteContent = string.Empty;

                key = Request.QueryString["uKey"];

                key = key.Replace("'", "");

                //hdnUserHash.Value = Request.Url.Segments[2];
                hdnUserHash.Value = key;


                //set cookie
                HttpCookie cookie = new HttpCookie(COOKIE_NAME);
                cookie.Value = key;
                Page.Response.Cookies.Add(cookie);



                //if the cache contains it, provide from cache, else use the DB
                if (HttpRuntime.Cache[CACHE_PREFIX + key] != null)
                {
                    divContent.InnerHtml = (string)HttpRuntime.Cache[CACHE_PREFIX +key ];
                }
                else
                {
                    try
                    {
                        SqlConnection conn = new SqlConnection(connString);
                        conn.Open();
                        SqlCommand cmd = new SqlCommand("NoteContent_s", conn);
                        SqlParameter param1 = new SqlParameter("@hashkey", SqlDbType.VarChar);
                        param1.Value = key;
                        SqlParameter param2 = new SqlParameter("@tabindex", SqlDbType.Int);
                        param2.Value = 0;

                        cmd.Parameters.Add(param1);
                        cmd.Parameters.Add(param2);

                        cmd.CommandType = CommandType.StoredProcedure;

                        SqlDataReader reader = cmd.ExecuteReader();

                        while (reader.Read())
                        {
                            NoteContent = reader["noteContent"].ToString();
                            break;
                        }
                    }
                    catch { }
                    divContent.InnerHtml = NoteContent;
                }
            }
        }


        static void Save(string data, string userHash)
        {

            /*  Trying to figure out a effective way of storing. If we are updating on everykeystroke, then its
             * going to be a lot of updates to the DB.
             * The other option is to use  
             * - Session and handle the Db save intermediately and on Session end
             * - Use a static variable, update and save conditionally
             * - Use the cache to update, save and handle the expired/removal event to store in the db            
             * */
            
            //going for the application cache option.The insert will insert/replace 
            //and the save to DB happens every minute.

            HttpRuntime.Cache.Insert(CACHE_PREFIX + userHash,
                                        data,
                                        null,
                                        Cache.NoAbsoluteExpiration,
                                        new TimeSpan(0, 0, 60),
                                        CacheItemPriority.Default,
                                        new CacheItemRemovedCallback(CacheItemRemovedCallback));
           
            //http://msdn.microsoft.com/en-us/library/7kxdx246.aspx            
            
        }


        public static void CacheItemRemovedCallback(String key, object value,CacheItemRemovedReason removedReason)
        {
            //everytime we updtate a already cached entry,
            //this event fires due the CacheItemRemovedReason.Removed reason[as the value got replaced]
            //We only need it on CacheItemRemovedReason.Expired
            if (removedReason != CacheItemRemovedReason.Removed)
            {
                SaveToDB((string)value, key.Replace(CACHE_PREFIX, ""));
            }
        }

        static void SaveToDB(string data, string userHash)
        {
            SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SQL"].ConnectionString);
            conn.Open();
            SqlCommand cmd = new SqlCommand("NoteContent_iu", conn);
            SqlParameter param1 = new SqlParameter("@hashkey", SqlDbType.VarChar);
            param1.Value = userHash;
            SqlParameter param2 = new SqlParameter("@tabindex", SqlDbType.Int);
            param2.Value = 0;
            SqlParameter param3 = new SqlParameter("@noteContent", SqlDbType.VarChar);
            param3.Value = data;

            cmd.Parameters.Add(param1);
            cmd.Parameters.Add(param2);
            cmd.Parameters.Add(param3);

            cmd.CommandType = CommandType.StoredProcedure;

            cmd.ExecuteNonQuery();

        }


        private string GenerateKey()
        {
            string key = KeyGenerator();

            if(CheckKeyExists(key))
            {
                //key exists, generate new one
                return GenerateKey();
            }
            else
            {
                return key;
            }
        }

        private bool CheckKeyExists(string key)
        {
            SqlConnection conn = new SqlConnection(connString);
            conn.Open();
            SqlCommand cmd = new SqlCommand("CheckKeyExists", conn);
            SqlParameter param1 = new SqlParameter("@hashkey", SqlDbType.VarChar);
            param1.Value = key;
            cmd.Parameters.Add(param1);
            cmd.CommandType = CommandType.StoredProcedure;

            string output = cmd.ExecuteScalar() as string;

            if (output == "1")
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public string KeyGenerator()
        {
            //The MD5 hash must be good enought of a unique key and there will be sufficient and unpredictable

            //Adding a random value to maximize entropy
            Random random = new Random(DateTime.Now.Millisecond);
            string seed = string.Empty;
            seed = DateTime.Now.AddMilliseconds(random.Next()).ToString();            
            //seed += randomStrings(DateTime.Now.Hour);
            MD5 md5 = MD5.Create();
            byte[] byteout = Encoding.Default.GetBytes(seed.ToArray());
            string hex = BitConverter.ToString(md5.ComputeHash(byteout));
            return(hex.Replace("-", ""));
        }


        //private string randomStrings(int length)
        //{
        //    if (length < 5)
        //        length = 12 - length;

        //    string randomString = string.Empty;

        //    Random rand = new Random();
        //    for (int i = 0; i < length; i++)
        //    {
        //        randomString+=char.ConvertFromUtf32(rand.Next(33, 127));
        //    }

        //    return randomString;
        //}








    }
}